package org.sws.infrastructure.persistent.repository;

import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RLock;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;
import org.sws.domain.activity.event.ActivitySkuStockZeroMessageEvent;
import org.sws.domain.activity.model.aggregate.CreateQuotaOrderAggregate;
import org.sws.domain.activity.model.aggregate.CreatePartakeOrderAggregate;
import org.sws.domain.activity.model.entity.*;
import org.sws.domain.activity.model.valobj.ActivitySkuStockKeyVO;
import org.sws.domain.activity.model.valobj.ActivityStateVO;
import org.sws.domain.activity.model.valobj.UserRaffleOrderStateVO;
import org.sws.domain.activity.repository.IActivityRepository;
import org.sws.infrastructure.event.EventPublisher;
import org.sws.infrastructure.persistent.dao.*;
import org.sws.infrastructure.persistent.po.*;
import org.sws.infrastructure.redis.IRedisService;
import org.sws.types.common.Constants;
import org.sws.types.enums.ResponseCode;
import org.sws.types.exception.AppException;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author sws
 * @Date 2025/3/18 21:13
 * @description:
 */
@Slf4j
@Repository
public class ActivityRepository implements IActivityRepository {
    @Resource
    private IRedisService redisService;
    @Resource
    private IRaffleActivityDao raffleActivityDao;
    @Resource
    private IRaffleActivitySkuDao raffleActivitySkuDao;
    @Resource
    private IRaffleActivityCountDao raffleActivityCountDao;
    @Resource
    private IRaffleActivityRechargeDao raffleActivityRechargeDao;
    @Resource
    private IRaffleActivityAccountDao raffleActivityAccountDao;
    @Resource
    private IRaffleActivityAccountMonthDao raffleActivityAccountMonthDao;
    @Resource
    private IRaffleActivityAccountDayDao raffleActivityAccountDayDao;
    @Resource
    private IUserRaffleOrderDao userRaffleOrderDao;
    @Resource
    private IUserCreditAccountDao userCreditAccountDao;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private IDBRouterStrategy dbRouterStrategy;
    @Resource
    private EventPublisher eventPublisher;
    @Resource
    private ActivitySkuStockZeroMessageEvent activitySkuStockZeroMessageEvent;

    @Override
    public ActivitySkuEntity queryActivitySku(Long sku) {
        RaffleActivitySku raffleActivitySku = raffleActivitySkuDao.queryActivitySku(sku);
        String cacheKey = Constants.RedisKey.ACTIVITY_SKU_STOCK_COUNT_SURPLUS_KEY + sku;
        Long atomicLong = redisService.getAtomicLong(cacheKey);
        if(atomicLong == null || atomicLong == 0){
            atomicLong = 0L;
        }
        return ActivitySkuEntity.builder()
                .sku(raffleActivitySku.getSku())
                .activityId(raffleActivitySku.getActivityId())
                .activityCountId(raffleActivitySku.getActivityCountId())
                .stockCount(raffleActivitySku.getStockCount())
                .stockCountSurplus(atomicLong.intValue())
                .productAmount(raffleActivitySku.getProductAmount())
                .build();
    }

    @Override
    public ActivityEntity queryRaffleActivityByActivityId(Long activityId) {
        // 优先从缓存获取
        String cacheKey = Constants.RedisKey.ACTIVITY_KEY + activityId;
        ActivityEntity activityEntity = redisService.getValue(cacheKey);
        if (null != activityEntity) return activityEntity;
        // 从库中获取数据
        RaffleActivity raffleActivity = raffleActivityDao.queryRaffleActivityByActivityId(activityId);
        activityEntity = ActivityEntity.builder()
                .activityId(raffleActivity.getActivityId())
                .activityName(raffleActivity.getActivityName())
                .activityDesc(raffleActivity.getActivityDesc())
                .beginDateTime(raffleActivity.getBeginDateTime())
                .endDateTime(raffleActivity.getEndDateTime())
                .strategyId(raffleActivity.getStrategyId())
                .state(ActivityStateVO.valueOf(raffleActivity.getState()))
                .build();
        redisService.setValue(cacheKey, activityEntity);
        return activityEntity;
    }
    @Override
    public ActivityEntity queryRaffleActivityByStrategyId(Long strategyId) {
        RaffleActivity raffleActivity = raffleActivityDao.queryRaffleActivityByStrategyId(strategyId);
        return ActivityEntity.builder()
                .activityId(raffleActivity.getActivityId())
                .activityName(raffleActivity.getActivityName())
                .activityDesc(raffleActivity.getActivityDesc())
                .beginDateTime(raffleActivity.getBeginDateTime())
                .endDateTime(raffleActivity.getEndDateTime())
                .strategyId(raffleActivity.getStrategyId())
                .state(ActivityStateVO.valueOf(raffleActivity.getState()))
                .build();
    }

    @Override
    public ActivityCountEntity queryRaffleActivityCountByActivityCountId(Long activityCountId) {
        // 优先从缓存获取
        String cacheKey = Constants.RedisKey.ACTIVITY_COUNT_KEY + activityCountId;
        ActivityCountEntity activityCountEntity = redisService.getValue(cacheKey);
        if (null != activityCountEntity) return activityCountEntity;
        // 从库中获取数据
        RaffleActivityCount raffleActivityCount = raffleActivityCountDao.queryRaffleActivityCountByActivityCountId(activityCountId);
        activityCountEntity = ActivityCountEntity.builder()
                .activityCountId(raffleActivityCount.getActivityCountId())
                .totalCount(raffleActivityCount.getTotalCount())
                .dayCount(raffleActivityCount.getDayCount())
                .monthCount(raffleActivityCount.getMonthCount())
                .build();
        redisService.setValue(cacheKey, activityCountEntity);
        return activityCountEntity;
    }

    @Override
    public void doSaveNoPayOrder(CreateQuotaOrderAggregate createQuotaOrderAggregate) {
        RLock lock = redisService.getLock(Constants.RedisKey.ACTIVITY_ACCOUNT_LOCK + createQuotaOrderAggregate.getUserId() + Constants.UNDERLINE + createQuotaOrderAggregate.getActivityId());
        try {
            lock.lock(3, TimeUnit.SECONDS);
            // 活动充值对象
            RaffleActivityRecharge raffleActivityRecharge = buildRaffleActivityRecharge(createQuotaOrderAggregate.getActivityRechargeEntity());
            // 账户对象
            RaffleActivityAccount raffleActivityAccount = new RaffleActivityAccount();
            raffleActivityAccount.setUserId(createQuotaOrderAggregate.getUserId());
            raffleActivityAccount.setActivityId(createQuotaOrderAggregate.getActivityId());
            raffleActivityAccount.setTotalCount(createQuotaOrderAggregate.getTotalCount());
            raffleActivityAccount.setTotalCountSurplus(createQuotaOrderAggregate.getTotalCount());
            raffleActivityAccount.setDayCount(createQuotaOrderAggregate.getDayCount());
            raffleActivityAccount.setDayCountSurplus(createQuotaOrderAggregate.getDayCount());
            raffleActivityAccount.setMonthCount(createQuotaOrderAggregate.getMonthCount());
            raffleActivityAccount.setMonthCountSurplus(createQuotaOrderAggregate.getMonthCount());
            // 账户对象 - 月
            RaffleActivityAccountMonth raffleActivityAccountMonth = new RaffleActivityAccountMonth();
            raffleActivityAccountMonth.setUserId(createQuotaOrderAggregate.getUserId());
            raffleActivityAccountMonth.setActivityId(createQuotaOrderAggregate.getActivityId());
            raffleActivityAccountMonth.setMonth(RaffleActivityAccountMonth.currentMonth());
            raffleActivityAccountMonth.setMonthCount(createQuotaOrderAggregate.getMonthCount());
            raffleActivityAccountMonth.setMonthCountSurplus(createQuotaOrderAggregate.getMonthCount());

            // 账户对象 - 日
            RaffleActivityAccountDay raffleActivityAccountDay = new RaffleActivityAccountDay();
            raffleActivityAccountDay.setUserId(createQuotaOrderAggregate.getUserId());
            raffleActivityAccountDay.setActivityId(createQuotaOrderAggregate.getActivityId());
            raffleActivityAccountDay.setDay(RaffleActivityAccountDay.currentDay());
            raffleActivityAccountDay.setDayCount(createQuotaOrderAggregate.getDayCount());
            raffleActivityAccountDay.setDayCountSurplus(createQuotaOrderAggregate.getDayCount());
            // 以用户ID作为切分键，通过 doRouter 设定路由【这样就保证了下面的操作，都是同一个链接下，也就保证了事务的特性】
            dbRouterStrategy.doRouter(createQuotaOrderAggregate.getUserId());
            // 编程式事务
            transactionTemplate.execute(action -> {
                try {
                    // 新增订单
                    raffleActivityRechargeDao.insert(raffleActivityRecharge);
                    // 更新用户活动账户
                    RaffleActivityAccount raffleActivityAccountRes = raffleActivityAccountDao.queryAccountByUserId(raffleActivityAccount);
                    if(null == raffleActivityAccountRes){
                        raffleActivityAccountDao.insert(raffleActivityAccount);
                    }else {
                        raffleActivityAccountDao.updateAccountQuota(raffleActivityAccount);
                    }
                    // 4. 更新账户 - 月
                    raffleActivityAccountMonthDao.addAccountQuota(raffleActivityAccountMonth);
                    // 5. 更新账户 - 日
                    raffleActivityAccountDayDao.addAccountQuota(raffleActivityAccountDay);
                    return 1;
                }catch (DuplicateKeyException e){
                    action.setRollbackOnly();
                    log.error("doSaveNoPayOrder，唯一索引冲突 userId: {} activityId: {} sku: {}", raffleActivityRecharge.getUserId(), raffleActivityRecharge.getActivityId(), raffleActivityRecharge.getSku(), e);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }catch (Exception e){
                    action.setRollbackOnly();
                    log.error("doSaveNoPayOrder方法异常， userId: {} activityId: {} sku: {}",createQuotaOrderAggregate.getUserId(), raffleActivityRecharge.getActivityId(), raffleActivityRecharge.getSku(), e);
                    throw new AppException(ResponseCode.UN_ERROR.getCode(), e);
                }
            });
        }finally {
            dbRouterStrategy.clear();
            lock.unlock();
        }
    }

    @Override
    public void doSaveCreditPayOrder(CreateQuotaOrderAggregate createQuotaOrderAggregate) {
        String userId = createQuotaOrderAggregate.getUserId();
        // 抽奖额度账户实体类
        RaffleActivityRecharge raffleActivityRecharge = buildRaffleActivityRecharge(createQuotaOrderAggregate.getActivityRechargeEntity());
        // 以用户ID作为切分键，通过 doRouter 设定路由【这样就保证了下面的操作，都是同一个链接下，也就保证了事务的特性】
        try {
            dbRouterStrategy.doRouter(userId);
            // 编程式事务
            transactionTemplate.executeWithoutResult(status -> {
                try {
                    raffleActivityRechargeDao.insert(raffleActivityRecharge);
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("doSaveCreditPayOrder，唯一索引冲突 userId: {} activityId: {} sku: {}",userId, raffleActivityRecharge.getActivityId(), raffleActivityRecharge.getSku(), e);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }catch (Exception e){
                    status.setRollbackOnly();
                    log.error("doSaveCreditPayOrder方法异常， userId: {} activityId: {} sku: {}",userId, raffleActivityRecharge.getActivityId(), raffleActivityRecharge.getSku(), e);
                    throw new AppException(ResponseCode.UN_ERROR.getCode(), e);
                }
            });
        } finally {
            dbRouterStrategy.clear();
        }
    }

    @Override
    public void updateRechargeOrder(DeliveryOrderEntity deliveryOrderEntity) {
        RLock lock = redisService.getLock(Constants.RedisKey.ACTIVITY_ACCOUNT_UPDATE_LOCK + deliveryOrderEntity.getUserId());
        try {
            // 将活动额度订单表待支付改为完成态，并将额度加到账户上
            RaffleActivityRecharge raffleActivityRechargeRes = new RaffleActivityRecharge();
            raffleActivityRechargeRes.setUserId(deliveryOrderEntity.getUserId());
            raffleActivityRechargeRes.setBizId(deliveryOrderEntity.getOutBusinessNo());
            RaffleActivityRecharge raffleActivityRecharge = raffleActivityRechargeDao.queryRaffleActivityRecharge(raffleActivityRechargeRes);
            if (null == raffleActivityRecharge) {
                return;
            }

            lock.lock(3, TimeUnit.SECONDS);
            // 账户对象 - 总
            RaffleActivityAccount raffleActivityAccount = new RaffleActivityAccount();
            raffleActivityAccount.setUserId(raffleActivityRecharge.getUserId());
            raffleActivityAccount.setActivityId(raffleActivityRecharge.getActivityId());
            raffleActivityAccount.setTotalCount(raffleActivityRecharge.getTotalCount());
            raffleActivityAccount.setTotalCountSurplus(raffleActivityRecharge.getTotalCount());
            raffleActivityAccount.setDayCount(raffleActivityRecharge.getDayCount());
            raffleActivityAccount.setDayCountSurplus(raffleActivityRecharge.getDayCount());
            raffleActivityAccount.setMonthCount(raffleActivityRecharge.getMonthCount());
            raffleActivityAccount.setMonthCountSurplus(raffleActivityRecharge.getMonthCount());

            // 账户对象 - 月
            RaffleActivityAccountMonth raffleActivityAccountMonth = new RaffleActivityAccountMonth();
            raffleActivityAccountMonth.setUserId(raffleActivityRecharge.getUserId());
            raffleActivityAccountMonth.setActivityId(raffleActivityRecharge.getActivityId());
            raffleActivityAccountMonth.setMonth(RaffleActivityAccountMonth.currentMonth());
            raffleActivityAccountMonth.setMonthCount(raffleActivityRecharge.getMonthCount());
            raffleActivityAccountMonth.setMonthCountSurplus(raffleActivityRecharge.getMonthCount());

            // 账户对象 - 日
            RaffleActivityAccountDay raffleActivityAccountDay = new RaffleActivityAccountDay();
            raffleActivityAccountDay.setUserId(raffleActivityRecharge.getUserId());
            raffleActivityAccountDay.setActivityId(raffleActivityRecharge.getActivityId());
            raffleActivityAccountDay.setDay(RaffleActivityAccountDay.currentDay());
            raffleActivityAccountDay.setDayCount(raffleActivityRecharge.getDayCount());
            raffleActivityAccountDay.setDayCountSurplus(raffleActivityRecharge.getDayCount());
            dbRouterStrategy.doRouter(deliveryOrderEntity.getUserId());
            // 编程式事务
            transactionTemplate.executeWithoutResult(status -> {
                try {
                    // 1. 更新订单
                    int updateCount = raffleActivityRechargeDao.updateOrderCompleted(raffleActivityRecharge);
                    if (1 != updateCount) {
                        status.setRollbackOnly();
                        return;
                    }
                    // 2. 更新账户 - 总
                    RaffleActivityAccount raffleActivityAccountRes = raffleActivityAccountDao.queryAccountByUserId(raffleActivityAccount);
                    if (null == raffleActivityAccountRes) {
                        raffleActivityAccountDao.insert(raffleActivityAccount);
                    } else {
                        raffleActivityAccountDao.updateAccountQuota(raffleActivityAccount);
                    }
                    // 4. 更新账户 - 月
                    raffleActivityAccountMonthDao.addAccountQuota(raffleActivityAccountMonth);
                    // 5. 更新账户 - 日
                    raffleActivityAccountDayDao.addAccountQuota(raffleActivityAccountDay);
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("更新订单记录，完成态，唯一索引冲突 userId: {} outBusinessNo: {}", deliveryOrderEntity.getUserId(), deliveryOrderEntity.getOutBusinessNo(), e);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }catch (Exception e){
                    status.setRollbackOnly();
                    log.error("updateRechargeOrder方法异常， userId: {} activityId: {} sku: {}",deliveryOrderEntity.getUserId(), raffleActivityRecharge.getActivityId(), raffleActivityRecharge.getSku(), e);
                    throw new AppException(ResponseCode.UN_ERROR.getCode(), e);
                }
            });
        } finally {
            dbRouterStrategy.clear();
            if(lock.isLocked()){
                lock.unlock();
            }
        }
    }

    private RaffleActivityRecharge buildRaffleActivityRecharge(ActivityRechargeEntity activityRechargeEntity) {
        RaffleActivityRecharge raffleActivityRecharge = new RaffleActivityRecharge();
        raffleActivityRecharge.setUserId(activityRechargeEntity.getUserId());
        raffleActivityRecharge.setSku(activityRechargeEntity.getSku());
        raffleActivityRecharge.setActivityId(activityRechargeEntity.getActivityId());
        raffleActivityRecharge.setActivityName(activityRechargeEntity.getActivityName());
        raffleActivityRecharge.setStrategyId(activityRechargeEntity.getStrategyId());
        raffleActivityRecharge.setRechargeId(activityRechargeEntity.getRechargeId());
        raffleActivityRecharge.setRechargeTime(activityRechargeEntity.getRechargeTime());
        raffleActivityRecharge.setTotalCount(activityRechargeEntity.getTotalCount());
        raffleActivityRecharge.setDayCount(activityRechargeEntity.getDayCount());
        raffleActivityRecharge.setMonthCount(activityRechargeEntity.getMonthCount());
        raffleActivityRecharge.setTotalCount(activityRechargeEntity.getTotalCount());
        raffleActivityRecharge.setDayCount(activityRechargeEntity.getDayCount());
        raffleActivityRecharge.setMonthCount(activityRechargeEntity.getMonthCount());
        raffleActivityRecharge.setPayAmount(activityRechargeEntity.getPayAmount());
        raffleActivityRecharge.setState(activityRechargeEntity.getState().getCode());
        raffleActivityRecharge.setBizId(activityRechargeEntity.getBizId());
        return raffleActivityRecharge;

    }

    @Override
    public boolean subtractSkuStock(Long sku, String key, Date endDateTime) {
        // 延迟队列进行redis上库存扣减
        long surplus = redisService.decr(key);
        if (surplus < 0) {
            // 库存小于0，恢复为0个
            redisService.setAtomicLong(key, 0);
            return false;
        }
        // 防止超扣，加锁兜底 如果后续有恢复库存，手动处理等【运营是人来操作，会有这种情况发放，系统要做防护】，也不会超卖。因为所有的可用库存key，都被加锁了。
        String lockKey = key + Constants.UNDERLINE + surplus;
        long lockTime = endDateTime.getTime() - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
        Boolean lock = null;
        try {
            lock = redisService.setNx(lockKey, lockTime, TimeUnit.MILLISECONDS);
            if(!lock){
                log.info("sku库存扣减加锁失败 {}", lockKey);
            }
        } catch (InterruptedException e) {
            log.warn("活动SKU库存扣减异常{}",lockKey,e);
        }
        if(surplus == 0){
            // 库存消耗没了以后，发送MQ消息，直接数据库更新成0，不走延时队列
            eventPublisher.publish(activitySkuStockZeroMessageEvent.topic(), activitySkuStockZeroMessageEvent.buildEventMessage(sku));
        }
        return Boolean.TRUE.equals(lock);
    }

    @Override
    public void activitySkuStockConsumeSendQueue(ActivitySkuStockKeyVO activitySkuStockKeyVO) {
        String cacheKey = Constants.RedisKey.ACTIVITY_SKU_COUNT_QUEUE_KEY + Constants.UNDERLINE + activitySkuStockKeyVO.getSku();
        RBlockingQueue<ActivitySkuStockKeyVO> blockingQueue = redisService.getBlockingQueue(cacheKey);
        RDelayedQueue<ActivitySkuStockKeyVO> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        delayedQueue.offer(activitySkuStockKeyVO, 3, TimeUnit.SECONDS);
    }

    @Override
    public ActivitySkuStockKeyVO takeQueueValue(Long sku) {
        String cacheKey = Constants.RedisKey.ACTIVITY_SKU_COUNT_QUEUE_KEY + Constants.UNDERLINE + sku;
        RBlockingQueue<ActivitySkuStockKeyVO> blockingQueue = redisService.getBlockingQueue(cacheKey);
        return blockingQueue.poll();
    }

    @Override
    public void updateActivitySkuStock(Long sku) {
        raffleActivitySkuDao.updateActivitySkuStock(sku);
    }

    @Override
    public void clearQueueValue(Long sku) {
        String cacheKey = Constants.RedisKey.ACTIVITY_SKU_COUNT_QUEUE_KEY + Constants.UNDERLINE + sku;
        RBlockingQueue<ActivitySkuStockKeyVO> blockingQueue = redisService.getBlockingQueue(cacheKey);
        RDelayedQueue<ActivitySkuStockKeyVO> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        delayedQueue.clear();
    }

    @Override
    public void clearActivitySkuStock(Long sku) {
        raffleActivitySkuDao.clearActivitySkuStock(sku);
    }

    @Override
    public void cacheActivitySkuStockCount(String cacheKey, Integer stockCount) {
        redisService.setValue(cacheKey, stockCount);
    }

    @Override
    public UserRaffleOrderEntity queryUnUsedUserRaffleOrder(String userId, Long activityId) {
        UserRaffleOrder userRaffleOrder = userRaffleOrderDao.queryUnUsedUserRaffleOrder(userId, activityId);
        if(null == userRaffleOrder){
            return null;
        }
        return UserRaffleOrderEntity.builder()
                .userId(userId)
                .activityId(activityId)
                .activityName(userRaffleOrder.getActivityName())
                .strategyId(userRaffleOrder.getStrategyId())
                .orderId(userRaffleOrder.getOrderId())
                .orderTime(userRaffleOrder.getOrderTime())
                .orderState(UserRaffleOrderStateVO.valueOf(userRaffleOrder.getOrderState()))
                .build();
    }

    @Override
    public ActivityAccountEntity queryActivityAccountByUserId(String userId, Long activityId) {
        return raffleActivityAccountDao.queryActivityAccountByUserId(userId, activityId);
    }

    @Override
    public ActivityAccountMonthEntity queryactivityMonthAccount(String userId, Long activityId, String month) {
        return raffleActivityAccountMonthDao.queryactivityMonthAccount(userId, activityId, month);
    }

    @Override
    public ActivityAccountDayEntity queryactivityDayAccount(String userId, Long activityId, String day) {
        return raffleActivityAccountDayDao.queryactivityDayAccount(userId, activityId, day);
    }

    @Override
    public List<ActivitySkuEntity> queryActivitySkuByActivityId(Long activityId) {
        List<RaffleActivitySku> raffleActivitySkus = raffleActivitySkuDao.queryActivitySkuListByActivityId(activityId);
        List<ActivitySkuEntity> activitySkuEntities = new ArrayList<>(raffleActivitySkus.size());
        for (RaffleActivitySku raffleActivitySku:raffleActivitySkus){
            ActivitySkuEntity activitySkuEntity = new ActivitySkuEntity();
            activitySkuEntity.setSku(raffleActivitySku.getSku());
            activitySkuEntity.setActivityCountId(raffleActivitySku.getActivityCountId());
            activitySkuEntity.setStockCount(raffleActivitySku.getStockCount());
            activitySkuEntity.setStockCountSurplus(raffleActivitySku.getStockCountSurplus());
            activitySkuEntities.add(activitySkuEntity);
        }
        return activitySkuEntities;
    }

    @Override
    public void saveCreatePartakeOrderAggregate(CreatePartakeOrderAggregate createPartakeOrderAggregate) {
        try {
            String userId = createPartakeOrderAggregate.getUserId();
            Long activityId = createPartakeOrderAggregate.getActivityId();
            ActivityAccountEntity activityAccountEntity = createPartakeOrderAggregate.getActivityAccountEntity();
            ActivityAccountMonthEntity activityAccountMonthEntity = createPartakeOrderAggregate.getActivityAccountMonthEntity();
            ActivityAccountDayEntity activityAccountDayEntity = createPartakeOrderAggregate.getActivityAccountDayEntity();
            UserRaffleOrderEntity userRaffleOrderEntity = createPartakeOrderAggregate.getUserRaffleOrderEntity();
            // 统一切换路由，以下事务内的所有操作，都走一个路由
            dbRouterStrategy.doRouter(userId);
            // 事务包裹 操作更新额度账户扣减和抽奖单创建
            transactionTemplate.execute(action -> {
                try {
                    // 更新总额度账户 扣减1
                    int accountImpactCount = raffleActivityAccountDao.updateAccountSubtractionQuota(
                            RaffleActivityAccount.builder()
                                    .userId(userId)
                                    .activityId(activityId)
                                    .build()
                    );
                    if(accountImpactCount != 1){
                        action.setRollbackOnly();
                        log.warn("写入创建参与活动记录，更新总账户额度不足，异常 userId: {} activityId: {}", userId, activityId);
                        throw new AppException(ResponseCode.ACCOUNT_QUOTA_ERROR.getCode(), ResponseCode.ACCOUNT_QUOTA_ERROR.getInfo());
                    }
                    // 创建或更新月额度账户
                    if(createPartakeOrderAggregate.isExistAccountMonth()){
                        int updateMonthCount = raffleActivityAccountMonthDao.updateActivityAccountMonthSubtractionQuota(
                                RaffleActivityAccountMonth.builder()
                                        .userId(userId)
                                        .activityId(activityId)
                                        .month(activityAccountMonthEntity.getMonth())
                                        .build());
                        if (1 != updateMonthCount) {
                            // 未更新成功则回滚
                            action.setRollbackOnly();
                            log.warn("写入参与活动记录，更新月账户额度不足，异常 userId: {} activityId: {} month: {}", userId, activityId, activityAccountMonthEntity.getMonth());
                            throw new AppException(ResponseCode.ACCOUNT_MONTH_QUOTA_ERROR.getCode(), ResponseCode.ACCOUNT_MONTH_QUOTA_ERROR.getInfo());
                        }
                        raffleActivityAccountDao.updateActivityAccountMonthSubtractionQuota(
                                RaffleActivityAccount.builder()
                                        .userId(userId)
                                        .activityId(activityId)
                                        .build());
                    }else {
                        raffleActivityAccountMonthDao.insert(
                                RaffleActivityAccountMonth.builder()
                                        .userId(userId)
                                        .activityId(activityId)
                                        .month(activityAccountMonthEntity.getMonth())
                                        .monthCount(activityAccountMonthEntity.getMonthCount())
                                        .monthCountSurplus(activityAccountMonthEntity.getMonthCountSurplus() - 1)
                                        .build());
                        // 更新新的一个月
                        raffleActivityAccountDao.updateActivityAccountMonthSurplusImageQuota(RaffleActivityAccount.builder()
                                .userId(userId)
                                .activityId(activityId)
                                .monthCountSurplus(activityAccountEntity.getMonthCount())
                                .build());
                    }
                    // 创建或更新日额度账户
                    if(createPartakeOrderAggregate.isExistAccountDay()){
                        int updateDayCount = raffleActivityAccountDayDao.updateActivityAccountDaySubtractionQuota(
                                RaffleActivityAccountDay.builder()
                                        .userId(userId)
                                        .activityId(activityId)
                                        .day(activityAccountDayEntity.getDay())
                                        .build());
                        if (1 != updateDayCount) {
                            // 未更新成功则回滚
                            action.setRollbackOnly();
                            log.warn("写入创建参与活动记录，更新日账户额度不足，异常 userId: {} activityId: {} day: {}", userId, activityId, activityAccountDayEntity.getDay());
                            throw new AppException(ResponseCode.ACCOUNT_DAY_QUOTA_ERROR.getCode(), ResponseCode.ACCOUNT_DAY_QUOTA_ERROR.getInfo());
                        }
                        raffleActivityAccountDao.updateActivityAccountDaySubtractionQuota(RaffleActivityAccount.builder()
                                .userId(userId)
                                .activityId(activityId)
                                .build());
                    }else {
                        raffleActivityAccountDayDao.insert(
                                RaffleActivityAccountDay.builder()
                                        .userId(userId)
                                        .activityId(activityId)
                                        .day(activityAccountDayEntity.getDay())
                                        .dayCount(activityAccountDayEntity.getDayCount())
                                        .dayCountSurplus(activityAccountDayEntity.getDayCountSurplus() - 1)
                                        .build());
                        raffleActivityAccountDao.updateActivityAccountDaySurplusImageQuota(RaffleActivityAccount.builder()
                                .userId(userId)
                                .activityId(activityId)
                                .dayCountSurplus(activityAccountEntity.getDayCount())
                                .build());
                    }
                    // 创建抽奖单
                    userRaffleOrderDao.insert(
                            UserRaffleOrder.builder()
                                    .userId(userId)
                                    .activityId(activityId)
                                    .activityName(userRaffleOrderEntity.getActivityName())
                                    .strategyId(userRaffleOrderEntity.getStrategyId())
                                    .orderId(userRaffleOrderEntity.getOrderId())
                                    .orderTime(userRaffleOrderEntity.getOrderTime())
                                    .orderState(userRaffleOrderEntity.getOrderState().getCode())
                                    .build()
                    );
                    return 1;
                } catch (DuplicateKeyException e) {
                    action.setRollbackOnly();
                    log.error("写入创建参与活动记录，唯一索引冲突 userId: {} activityId: {}", userId, activityId, e);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }catch (Exception e){
                    action.setRollbackOnly();
                    log.error("创建用户抽奖单异常 useId: {},activityId: {}", userId, activityId, e);
                    throw new AppException(ResponseCode.UN_ERROR.getCode(), e);
                }
            });
        } finally {
            dbRouterStrategy.clear();
        }
    }

    @Override
    public Integer queryRaffleActivityAccountTotalPartakeCount(Long activityId, String userId) {
        RaffleActivityAccount raffleActivityAccount = new RaffleActivityAccount();
        raffleActivityAccount.setActivityId(activityId);
        raffleActivityAccount.setUserId(userId);
        Integer totalPartakeCount = raffleActivityAccountDao.queryRaffleActivityAccountTotalPartakeCount(raffleActivityAccount);
        return null == totalPartakeCount ? 0 : totalPartakeCount;
    }

    @Override
    public Integer queryRaffleActivityAccountMonthPartakeCount(Long activityId, String userId) {
        RaffleActivityAccountMonth raffleActivityAccountMonth = new RaffleActivityAccountMonth();
        raffleActivityAccountMonth.setActivityId(activityId);
        raffleActivityAccountMonth.setUserId(userId);
        raffleActivityAccountMonth.setMonth(RaffleActivityAccountMonth.currentMonth());
        Integer monthPartakeCount = raffleActivityAccountMonthDao.queryRaffleActivityAccountMonthPartakeCount(raffleActivityAccountMonth);
        return null == monthPartakeCount ? 0 : monthPartakeCount;
    }

    @Override
    public Integer queryRaffleActivityAccountDayPartakeCount(Long activityId, String userId) {
        RaffleActivityAccountDay raffleActivityAccountDay = new RaffleActivityAccountDay();
        raffleActivityAccountDay.setActivityId(activityId);
        raffleActivityAccountDay.setUserId(userId);
        raffleActivityAccountDay.setDay(RaffleActivityAccountDay.currentDay());
        Integer dayPartakeCount = raffleActivityAccountDayDao.queryRaffleActivityAccountDayPartakeCount(raffleActivityAccountDay);
        return null == dayPartakeCount ? 0 : dayPartakeCount;
    }

    @Override
    public int queryActivitySkuSurplus(Long sku) {
        RaffleActivitySku raffleActivitySku = raffleActivitySkuDao.queryActivitySku(sku);
        if(null == raffleActivitySku){
            return 0;
        }
        return raffleActivitySku.getStockCountSurplus() < 0 ? 0 : raffleActivitySku.getStockCountSurplus();
    }

    @Override
    public ActivityAccountEntity queryAccountSurplus(String userId, Long activityId) {
        ActivityAccountEntity activityTotalAccountEntity = raffleActivityAccountDao.queryActivityAccountByUserId(userId, activityId);
        if(Objects.isNull(activityTotalAccountEntity)){
            return ActivityAccountEntity.builder()
                    .activityId(activityId)
                    .userId(userId)
                    .totalCount(0)
                    .totalCountSurplus(0)
                    .monthCount(0)
                    .monthCountSurplus(0)
                    .dayCount(0)
                    .dayCountSurplus(0)
                    .build();
        }
        // 2. 查询月账户额度
        RaffleActivityAccountMonth raffleActivityAccountMonth = raffleActivityAccountMonthDao.queryActivityAccountMonthByUserId(RaffleActivityAccountMonth.builder()
                .activityId(activityId)
                .userId(userId)
                .month(RaffleActivityAccountMonth.currentMonth())
                .build());

        // 3. 查询日账户额度
        RaffleActivityAccountDay raffleActivityAccountDay = raffleActivityAccountDayDao.queryActivityAccountDayByUserId(RaffleActivityAccountDay.builder()
                .activityId(activityId)
                .userId(userId)
                .day(RaffleActivityAccountDay.currentDay())
                .build());

        // 组装对象
        ActivityAccountEntity activityAccountEntity = new ActivityAccountEntity();
        activityAccountEntity.setUserId(userId);
        activityAccountEntity.setActivityId(activityId);
        activityAccountEntity.setTotalCount(activityTotalAccountEntity.getTotalCount());
        activityAccountEntity.setTotalCountSurplus(activityTotalAccountEntity.getTotalCountSurplus());

        // 如果没有创建日账户，则从总账户中获取日总额度填充。「当新创建日账户时，会获得总账户额度」
        if (null == raffleActivityAccountDay) {
            activityAccountEntity.setDayCount(activityTotalAccountEntity.getDayCount());
            activityAccountEntity.setDayCountSurplus(activityTotalAccountEntity.getDayCount());
        } else {
            activityAccountEntity.setDayCount(raffleActivityAccountDay.getDayCount());
            activityAccountEntity.setDayCountSurplus(raffleActivityAccountDay.getDayCountSurplus());
        }

        // 如果没有创建月账户，则从总账户中获取月总额度填充。「当新创建日账户时，会获得总账户额度」
        if (null == raffleActivityAccountMonth) {
            activityAccountEntity.setMonthCount(activityTotalAccountEntity.getMonthCount());
            activityAccountEntity.setMonthCountSurplus(activityTotalAccountEntity.getMonthCount());
        } else {
            activityAccountEntity.setMonthCount(raffleActivityAccountMonth.getMonthCount());
            activityAccountEntity.setMonthCountSurplus(raffleActivityAccountMonth.getMonthCountSurplus());
        }
        return activityAccountEntity;
    }

    @Override
    public List<SkuProductEntity> querySkuProductEntityListByActivityId(Long activityId) {
        List<RaffleActivitySku> raffleActivitySkus = raffleActivitySkuDao.queryActivitySkuListByActivityId(activityId);
        List<SkuProductEntity> skuProductEntities = new ArrayList<>(raffleActivitySkus.size());
        for (RaffleActivitySku raffleActivitySku : raffleActivitySkus) {
            RaffleActivityCount raffleActivityCount = raffleActivityCountDao.queryRaffleActivityCountByActivityCountId(raffleActivitySku.getActivityCountId());

            SkuProductEntity.ActivityCount activityCount = new SkuProductEntity.ActivityCount();
            activityCount.setTotalCount(raffleActivityCount.getTotalCount());
            activityCount.setMonthCount(raffleActivityCount.getMonthCount());
            activityCount.setDayCount(raffleActivityCount.getDayCount());

            skuProductEntities.add(SkuProductEntity.builder()
                    .sku(raffleActivitySku.getSku())
                    .activityId(raffleActivitySku.getActivityId())
                    .activityCountId(raffleActivitySku.getActivityCountId())
                    .stockCount(raffleActivitySku.getStockCount())
                    .stockCountSurplus(raffleActivitySku.getStockCountSurplus())
                    .productAmount(raffleActivitySku.getProductAmount())
                    .activityCount(activityCount)
                    .build());

        }
        return skuProductEntities;
    }

    @Override
    public UnpaidActivityOrderEntity queryUnpaidActivityOrder(SkuRechargeEntity skuRechargeEntity) {
        RaffleActivityRecharge raffleActivityOrderReq = new RaffleActivityRecharge();
        raffleActivityOrderReq.setUserId(skuRechargeEntity.getUserId());
        raffleActivityOrderReq.setSku(skuRechargeEntity.getSku());
        RaffleActivityRecharge raffleActivityOrderRes = raffleActivityRechargeDao.queryUnpaidActivityOrder(raffleActivityOrderReq);
        if (null == raffleActivityOrderRes) return null;
        return UnpaidActivityOrderEntity.builder()
                .userId(raffleActivityOrderRes.getUserId())
                .orderId(raffleActivityOrderRes.getRechargeId())
                .outBusinessNo(raffleActivityOrderRes.getBizId())
                .payAmount(raffleActivityOrderRes.getPayAmount())
                .build();
    }

    @Override
    public BigDecimal queryUserCreditAccountAmount(String userId) {
        try {
            dbRouterStrategy.doRouter(userId);
            UserCreditAccount userCreditAccountReq = new UserCreditAccount();
            userCreditAccountReq.setUserId(userId);
            UserCreditAccount userCreditAccount = userCreditAccountDao.queryUserCreditAccount(userCreditAccountReq);
            if (null == userCreditAccount){
                return BigDecimal.ZERO;
            }
            return userCreditAccount.getAvailableAmount();
        } finally {
            dbRouterStrategy.clear();
        }
    }

    @Override
    public List<Long> querySkuList() {
        return raffleActivitySkuDao.querySkuList();
    }

}
